#include "StatusGrpcClient.h"
#include "global.h"
#include "data.h"

StatusConPool::StatusConPool(size_t poolSize, string host, string port)
    : _poolSize(poolSize), _host(host), _port(port), _b_stop(false) {
  for (size_t i = 0; i < _poolSize; i++) {
    shared_ptr<Channel> channel = grpc::CreateChannel(
        _host + ":" + _port, grpc::InsecureChannelCredentials());
    _connections.push(StatusService::NewStub(channel));
  }
}

StatusConPool::~StatusConPool() {
    std::lock_guard<std::mutex> lock(_mutex);
    close();
    while (!_connections.empty()) {
        _connections.pop();
    }
}

void StatusConPool::close() {
    _b_stop = true;
    _cond.notify_all();
}

unique_ptr<StatusService::Stub> StatusConPool::getConnection() {
  std::unique_lock<std::mutex> lock(_mutex);
  _cond.wait(lock, [this]() { 
    if (_b_stop) {
      return true;
    }
    return !_connections.empty();
  });

  if (_b_stop) {
    return nullptr;
  }

  auto conn = std::move(_connections.front());
  _connections.pop();
  return conn;
}

void StatusConPool::releaseConnection(unique_ptr<StatusService::Stub> connection) {
    std::lock_guard<std::mutex> lock(_mutex);
    if (_b_stop) {
      return;
    }

  _connections.push(std::move(connection));
  _cond.notify_one();
}

StatusGrpcClient::~StatusGrpcClient() {}


GetChatServerRsp StatusGrpcClient::GetChatServer(int uid) {
  ClientContext context;
  GetChatServerRsp reply;
  GetChatServerReq request;
  request.set_uid(uid);
  auto stub = _pool->getConnection();
  Status status = stub->GetChatServer(&context, request, &reply);
  Defer defer([&stub, this]() { 
      _pool->releaseConnection(std::move(stub)); 
  });
  if (status.ok()) {
    return reply;
  } else {
    reply.set_error(ErrorCodes::RPCFailed);
    return reply;
  }
}

LoginRsp StatusGrpcClient::Login(int uid, std::string token) {
  ClientContext context;
  LoginRsp reply;
  LoginReq request;
  request.set_uid(uid);
  request.set_token(token);

  auto stub = _pool->getConnection();
  Status status = stub->Login(&context, request, &reply);
  Defer defer([&stub, this]() { _pool->releaseConnection(std::move(stub)); });
  if (status.ok()) {
    return reply;
  } else {
    reply.set_error(ErrorCodes::RPCFailed);
    return reply;
  }
}

StatusGrpcClient::StatusGrpcClient() {
  auto& gCfgMgr = ConfigMgr::GetInstance();
  std::string host = gCfgMgr["StatusServer"]["Host"];
  std::string port = gCfgMgr["StatusServer"]["Port"];
  _pool.reset(new StatusConPool(5, host, port));
}
